
' NOT MAINTAIN
Imports System.Threading
Imports std

Public Class cacheManager2(Of nameType As IComparable(Of nameType), valueType)

    Private Sub assert(ByVal v As Boolean, Optional ByVal msg As String = Nothing)
        If Not v Then
            writeConsoleline(ToString())
            _debug.assert(v, msg)
        End If
    End Sub

    Private Class cachepair
        Inherits pair(Of reverseDate, valueType)
        Implements IComparable(Of cachepair)

        Public Overloads Function CompareTo(ByVal other As cachepair) As Int32 _
            Implements IComparable(Of cachepair).CompareTo
            If other Is Nothing Then
                Return compare(first, CType(Nothing, reverseDate))
            Else
                Return compare(first, other.first)
            End If
        End Function
    End Class

    Private Function make_pair(ByVal d As reverseDate, ByVal v As valueType) As cachepair
        'this should only be called by mapheap.insert, which will copy the pair, so save a copy, use only reference
        Dim rtn As cachepair = Nothing
        rtn = New cachepair()
        rtn.first = d
        rtn.second = v
        Return rtn
    End Function

    Public Const defaultCacheSize As Int64 = 1024
    Public Const defaultRetireTicks As Int64 = npos
    Public Const defaultThreadsafe As Boolean = False

    Private _mapheap As mapheap(Of nameType, cachepair) = Nothing
    Private _size As Int64 = defaultCacheSize
    Private _lock As Object = Nothing
    Private _retireticks As Int64 = defaultRetireTicks

    Private Sub initial(Optional ByVal threadsafe As Boolean = defaultThreadsafe, _
                        Optional ByVal cachesize As Int64 = defaultCacheSize, _
                        Optional ByVal retireticks As Int64 = defaultRetireTicks)
        If isdebugmode() AndAlso Not GetType(valueType).IsValueType AndAlso Not isCloneable(Of valueType)() Then
            raiseError("cache type which is not cloneable " + GetType(valueType).FullName() _
                       + ", may cause data lost or corrupt.", errorHandle.errorType.exclamation)
        End If

        _mapheap = New mapheap(Of nameType, cachepair)()
        If threadsafe Then
            _lock = New Object()
        End If
        _size = cachesize
        _retireticks = retireticks
    End Sub

    Public Sub New(ByVal threadsafe As Boolean, ByVal cachesize As Int64, ByVal retireticks As Int64)
        initial(threadsafe, cachesize, retireticks)
    End Sub

    Public Function retireticks() As Int64
        Return _retireticks
    End Function

    Public Function lock() As Object
        Return _lock
    End Function

    Public Function cachesize() As Int64
        Return _size
    End Function

    Private Sub startmutex()
        If Not lock() Is Nothing Then
            Monitor.Enter(lock())
        End If
    End Sub

    Private Sub endmutex()
        If Not lock() Is Nothing Then
            Monitor.Exit(lock())
        End If
    End Sub

    Public Sub add(ByVal name As nameType, ByVal value As valueType)
        If Not name Is Nothing Then
            startmutex()
            _mapheap.insert(name, make_pair(DateTime.Now, value))
            If size() > cachesize() Then
                Dim oldname As nameType = Nothing
                _mapheap.pop_front(Nothing, Nothing)
            End If
            endmutex()
        End If
    End Sub

    Public Function have(ByVal name As nameType) As Boolean
        Return [get](name, Nothing)
    End Function

    Public Function [get](ByVal name As nameType, ByRef value As valueType) As Boolean
        If Not name Is Nothing Then
            Dim rtn As Boolean = False

            startmutex()
            Dim it As mapheap(Of nameType, cachepair).iterator
            it = _mapheap.find(name)
            If it <> _mapheap.end() Then
                If retireticks() > 0 Then
                    If (Now.Ticks - (+it).first.first.data.Ticks) < retireticks() Then
                        copy(value, (+it).first.second)
                        rtn = True
                    Else
                        eraseImpl(name)
                        rtn = False
                    End If
                Else
                    copy(value, (+it).first.second)
                    _mapheap.insert(name, make_pair(DateTime.Now, (+it).first.second))
                    rtn = True
                End If
            End If
            endmutex()

            Return rtn
        Else
            Return False
        End If
    End Function

    Public Function [get](ByVal name As nameType) As valueType
        Dim rtn As valueType = Nothing
        [get](name, rtn)
        Return rtn
    End Function

    Public Sub clear()
        startmutex()
        _mapheap.clear()
        If isdebugmode() Then
            assert(empty(), "cannot clear.")
        End If
        endmutex()
    End Sub

    Private Function eraseImpl(ByVal name As nameType) As Boolean
        Return _mapheap.erase(name)
    End Function

    Public Function [erase](ByVal name As nameType) As Boolean
        Dim rtn As Boolean = False
        startmutex()
        rtn = eraseImpl(name)
        endmutex()
        Return rtn
    End Function

    Public Function size() As Int64
        Return _mapheap.size()
    End Function

    Public Function empty() As Boolean
        Return _mapheap.empty()
    End Function

    Public Overrides Function ToString() As String
        Return Convert.ToString(_mapheap)
    End Function

    Public Shared Sub unittest()
        Console.WriteLine("**** cacheManagerUnitTest *****")

        Dim firstRound As Boolean = True
        Dim cache As cacheManager2(Of String, Int32) = Nothing
        cache = New cacheManager2(Of String, Int32)(cacheManager2(Of String, Int32).defaultThreadsafe, _
                                                    4, cacheManager2(Of String, Int32).defaultRetireTicks)
        Dim key As String = Nothing
        Dim value As Double = 0
retry:
        Console.Write("input key string ")
        key = Console.ReadLine
        While Not key Is Nothing
            Dim str As String = Nothing
            Do
                Console.Write("input value double ")
                str = Console.ReadLine
            Loop Until convertor.todouble(str, value)
            cache.add(key, value)
            Console.Write("input key string ")
            key = Console.ReadLine
        End While

        Console.WriteLine("cache.size() = " + Convert.ToString(cache.size()))
        Console.WriteLine("cache.empty() = " + Convert.ToString(cache.empty()))
        Console.WriteLine("output elements.")
        Console.Write("input key string ")
        key = Console.ReadLine
        While Not key Is Nothing
            If cache.get(key, value) Then
                Console.WriteLine(key + character.tab + Convert.ToString(value))
            Else
                Console.WriteLine("not existing " + key + " in cacheManager2")
            End If
            Console.Write("input key string ")
            key = Console.ReadLine
        End While

        If firstRound Then
            firstRound = False
            Console.WriteLine("using clear method, and do again.")
            cache.clear()
            GoTo retry
        End If
        Console.WriteLine("********************************")
    End Sub

    Private Class automaticalCacheUnittestInfo
        Public cache As pointer(Of cacheManager2(Of nameType, valueType)) = Nothing
        Public keys As pointer(Of vector(Of nameType)) = Nothing
    End Class

    Private Shared Sub automaticalCache(ByVal obj As Object)
        Dim roundcount As Int64 = 1024
        If isreleasebuild() Then
            roundcount *= 20
        End If

        Dim info As cacheManager2(Of String, bytes).automaticalCacheUnittestInfo = Nothing
        info = convertor.cast(Of cacheManager2(Of String, bytes).automaticalCacheUnittestInfo)(obj)
        Dim cache As cacheManager2(Of String, bytes) = Nothing
        Dim keys As vector(Of String) = Nothing
        cache = +(info.cache)
        keys = +(info.keys)

        Dim i As Int64
        For i = 0 To roundcount - 1
            Dim j As Int64
            j = rnd(0, keys.size(), True)
            If rnd(0, 2, True) = 0 Then
                Dim s As String = Nothing
                s = createGUID()
                raiseError("add " + keys(j) + " with data " + s)
                For k As Int64 = 0 To 9
                    s += s
                Next
                cache.add(keys(j), encoding.utf8().GetBytes(s))
            Else
                raiseError("get " + keys(j))
                cache.get(keys(j), Nothing)
            End If
            sleepTicks(rnd(0, millisecondsToTicks(1), True))
        Next
    End Sub

    Public Shared Sub automaticalUnittest()
        Const cachesize As Int64 = 1024
        Const threadcount As Int64 = 16

        raiseError("********** cacheManager2 Automatical Unittest **********", errorHandle.errorType.application)

        raiseError("prepare keys vector")
        Dim keys As vector(Of String) = Nothing
        keys = New vector(Of String)()
        Dim i As Int64
        For i = 0 To (cachesize << 2) - 1
            keys.push_back(strleft(createGUID(), 5))
        Next

        Dim round As Int64
        For round = 0 To 2
            Dim cache As cacheManager2(Of String, bytes) = Nothing
            If round = 0 Then
                raiseError("retire ticks set to 4*1000")
                cache = New cacheManager2(Of String, bytes)(True, cachesize, 4 * 1000)
            ElseIf round = 1 Then
                raiseError("retire ticks set to -1")
                cache = New cacheManager2(Of String, bytes)(True, cachesize, npos)
            Else
                raiseError("retire ticks set to 1")
                cache = New cacheManager2(Of String, bytes)(True, cachesize, 1)
            End If

            Dim info As cacheManager2(Of String, bytes).automaticalCacheUnittestInfo = Nothing
            info = New cacheManager2(Of String, bytes).automaticalCacheUnittestInfo()
            info.cache = New pointer(Of cacheManager2(Of String, bytes))(cache)
            info.keys = New pointer(Of vector(Of String))(keys)

            raiseError("start automaticalCache threads")
            Dim threads(threadcount - 1) As Thread
            For i = 0 To threadcount - 1
                threads(i) = startThread(AddressOf automaticalCache, info)
            Next

            For i = 0 To threadcount - 1
                _debug.assert(Not threads(i) Is Nothing, "thread(" + Convert.ToString(i) + ") is nothing.")
                threads(i).Join()
            Next
        Next

        raiseError("******* finish cacheManager2 Automatical Unittest *******", errorHandle.errorType.application)
    End Sub
End Class
